package com.mjk.manager.magicapi.plugin.webservice.proxy;

import com.mjk.common.tools.gson.GsonUtils;
import com.mjk.common.tools.logger.BizLogger;
import com.mjk.common.tools.xml.XmlUtils;
import com.mjk.common.util.StrUtils;
import com.mjk.common.web.getway.restful.prototal.ws.soap.annotation.BeeSoapMethod;
import com.mjk.manager.magicapi.core.core.model.MagicEntity;
import com.mjk.manager.magicapi.plugin.webservice.model.WebServiceInfo;
import groovy.lang.GroovyClassLoader;
import jakarta.jws.WebParam;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class MagicWebServiceProxy {

    /**
     * 自定义newProxyInstance方法，传入自定义TXClassLoader
     *
     * @param interfaces Class<?>[]
     */
    public static Object newProxyInstance(Class<?>[] interfaces, GroovyClassLoader groovyClassLoader, MagicEntity entity) {

        if (null == groovyClassLoader) {
            groovyClassLoader = new GroovyClassLoader();
        }
        // 1、生成源代码
        String proxySrc = generateSrc(interfaces[0], (WebServiceInfo)entity);
        //System.out.println(proxySrc);
        groovyClassLoader.addClasspath(interfaces[0].getName());
        //System.out.println(proxySrc);
        //里面的文本是Java代码,但是我们可以看到这是一个字符串我们可以直接生成对应的Class<?>对象,而不需要我们写一个.java文件
        Class<?> clazz = groovyClassLoader.parseClass(proxySrc);
        // 5.返回被代理后的代理对象
        try {
            Constructor<?> instance = clazz.getConstructor(MagicEntity.class);
            return instance.newInstance(entity);
        } catch (Exception e) {
            //do noting
            BizLogger.error(e);
        } finally {
            try {
                Objects.requireNonNull(groovyClassLoader).close();
            } catch (IOException e) {
                //do noting
                BizLogger.error(e);
            }
        }
        return null;
    }

    /**
     * 生成源代码
     */
    private static String generateSrc(Class<?> interfaces, WebServiceInfo entity) {
        // 换行符
        String ln = "\r\n";
        StringBuilder src = new StringBuilder("package " + interfaces.getPackage().getName() + ";").append(ln);
        src.append(
                """
                        import com.mjk.manager.magicapi.core.utils.ScriptManager;
                        import org.ssssssss.script.MagicScriptContext;
                        import com.mjk.common.base.error.BizException;
                        import com.mjk.common.web.getway.constns.GetWayError;
                        import java.lang.reflect.Method;
                        import jakarta.jws.WebService;
                        import cn.hutool.core.util.XmlUtil;
                        import com.mjk.common.tools.xml.XmlUtils;
                        import com.mjk.common.tools.gson.GsonUtils;
                        import com.mjk.common.web.getway.restful.prototal.ws.soap.annotation.BeeSoapMethod;
                        import com.mjk.common.web.getway.restful.prototal.ws.soap.annotation.BeeSoapMethod.ResponseType;
                        import jakarta.jws.WebParam;
                        import jakarta.jws.WebMethod;
                        import com.mjk.common.tools.logger.BizLogger; 
                        import java.lang.reflect.InvocationHandler;
                        import com.mjk.manager.magicapi.core.core.model.MagicEntity;
                        import com.mjk.manager.magicapi.core.core.logging.LogbackLoggerContext;
                        """
        );
        src.append("import ").append(interfaces.getName()).append(";").append(ln);
        src.append("@WebService(targetNamespace=\"http://").append(getUnSeqPackage(interfaces.getPackage().getName())).append("/\")").append(ln);
        src.append("public class ").append(interfaces.getSimpleName()).append("$Proxy0 implements ").append(interfaces.getSimpleName()).append("{").append(ln);

        src.append("MagicEntity entity;" + ln);
        src.append("public ").append(interfaces.getSimpleName()).append("$Proxy0(MagicEntity entity) {").append(ln);
        src.append("this.entity = entity;" + ln);
        src.append("}" + ln);
        for (Method m : interfaces.getMethods()) {
            List<String> paramsStr = new ArrayList<>();
            List<String> clazzStr = new ArrayList<>();
            List<String> paramsValue = new ArrayList<>();
            for (Parameter p : m.getParameters()) {
                WebParam param = p.getAnnotation(WebParam.class);
                if (param == null) {
                    paramsValue.add(p.getName());
                    paramsStr.add(p.getParameterizedType().getTypeName() + " " + p.getName());
                } else {
                    paramsValue.add(param.name());
                    paramsStr.add(p.getParameterizedType().getTypeName() + " " + param.name());
                }
                clazzStr.add(p.getParameterizedType().getTypeName() + ".class");
            }

            src.append("@WebMethod" + ln);
            BeeSoapMethod soapMethod = m.getAnnotation(BeeSoapMethod.class);
            if (null != soapMethod) {
                src.append("@BeeSoapMethod(work=\"").append(soapMethod.work()).append("\",method=\"").append(soapMethod.method()).append("\",reqType=ResponseType.").append(soapMethod.reqType()).append(",rspType=ResponseType.").append(soapMethod.rspType()).append(")").append(ln);
            }
            src.append("public ").append(m.getReturnType().getName()).append(" ").append(m.getName()).append("(").append(StrUtils.join(paramsStr, ",")).append("){").append(ln);
            if(!CollectionUtils.isEmpty(paramsValue)) {
                src.append(" BizLogger.info(\"参数为：\"+" + StrUtils.join(paramsValue, ",") + ");").append(ln);
            }
            if(BeeSoapMethod.ResponseType.XML.ordinal()+1 == Integer.valueOf(entity.getInType())) {
                src.append(" Map<String,Object> params = getParams(ResponseType.XML," + StrUtils.join(paramsValue, ",") + ");").append(ln);
            }else if(BeeSoapMethod.ResponseType.JSON.ordinal()+1 == Integer.valueOf(entity.getInType())){
                src.append(" Map<String,Object> params = getParams(ResponseType.JSON," + StrUtils.join(paramsValue, ",") + ");").append(ln);
            }
            src.append(""" 
                    LogbackLoggerContext.startLog(params,"2");
                    MagicScriptContext magicScriptContext = new MagicScriptContext(params);
                    magicScriptContext.setScriptName(entity.getName());
                    Object obj = ScriptManager.executeScript(entity.getScript(), magicScriptContext);                  
                    """);
            if(BeeSoapMethod.ResponseType.XML.ordinal()+1 == Integer.valueOf(entity.getOutType())) {
                src.append(" if(obj instanceof String){").append(ln);
                src.append("  LogbackLoggerContext.endLog(obj,entity.getName());").append(ln);
                src.append(" return obj;").append(ln);
                src.append(" }else{").append(ln);
                src.append("  LogbackLoggerContext.endLog(obj,entity.getName());").append(ln);
                src.append(" return XmlUtils.beanToXml(obj,false);").append(ln);
                src.append(" }").append(ln);
            }else if(BeeSoapMethod.ResponseType.JSON.ordinal()+1 == Integer.valueOf(entity.getOutType())){
                src.append(" return GsonUtils.bean2Json(obj); ").append(ln);
            }
            src.append("}" + ln);
        }

        src.append("""
                public Map<String,Object> getParams(BeeSoapMethod.ResponseType type, String... args){
                        Map<String,Object> params = new HashMap<>();
                        for(String arg : args) {
                            if (type.equals(BeeSoapMethod.ResponseType.JSON)) {
                                if (GsonUtils.isJson(arg)) {
                                    params.putAll(GsonUtils.gson2Map(arg));
                                } else {
                                    throw new BizException(GetWayError.DATA_FORMAT_ERROR.getMsg());
                                }
                            } else if (type.equals(BeeSoapMethod.ResponseType.XML)) {
                                if (XmlUtils.isXML(arg)) {
                                    Map<String, Object> tmpMap = XmlUtil.xmlToMap(arg);
                                    if (tmpMap.containsKey("root")) {
                                        params.putAll((Map<String, Object>) tmpMap.get("root"));
                                    } else {
                                        params.putAll(tmpMap);
                                    }
                                } else {
                                    throw new BizException(GetWayError.DATA_FORMAT_ERROR.getMsg());
                                }
                            }
                        }
                        return params;
                    }
                """);
        src.append("}");
        return src.toString();
    }


    public static String getUnSeqPackage(String packageName) {
        String[] packages = packageName.split("\\.");
        StringBuilder sb = new StringBuilder();
        for (int i = packages.length - 1; i >= 0; i--) {
            sb.append(packages[i]).append(".");
        }
        String result = sb.toString();
        if (result.endsWith(".")) {
            result = result.substring(0, result.length() - 1);
        }
        return result;
    }

}